home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 January: Mac OS SDK / Dev.CD Jan 98 SDK2.toast / Development Kits (Disc 2) / Thread Manager / ThreadUtilities / Util Headers & Source / ThreadUtil.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-17  |  19.6 KB  |  692 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        ThreadUtil.c
  3.  
  4.     Contains:    Routines for Thread Manager Utilities
  5.  
  6.     Written by:    Brad Post, Eric Anderson and Bill Knott
  7.  
  8.     Copyright:    © 1993 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.                 5/8/93        bsp        Added the initialization of busy to the CreateLinkedListSemaphore.
  13.                 
  14.                 4/19/93        bsp        Added the code to handle the busy boolean in GetLinkedListSemaphore and
  15.                                     ReleaseLinkedListSemaphore.
  16.                 
  17.                 3/3/93        bsp        Added code to handle the lastInList element of the LLSemaphores.
  18.                 2/19/93        bsp        Fixed a bug in DeleteArraySemaphore, I was off by one Ptr, so was getting 
  19.                                     a MemErr == -111 (WhichZone failed).
  20.                 2/16/93        bsp        Added the pragma segment.  Fixed a bug in ReleaseLL and ReleaseArray, so it makes the
  21.                                     semaphore really available.  Added funtion InitializeThreadUtilities.
  22.                 2/9/93        bsp        Cleaned up the interface.
  23.                 2/8/93        bsp        Changed functions to use anArraySemaphorePtr.  Modified CreateArraySemaphore to use
  24.                                     anArraySemaphorePtr * and create the semaphore.  Also do a check if maxInQueue is
  25.                                     a valid number > 0 else return a threadProtocolErr.  Changed DeleteArraySemaphore to
  26.                                     delete the array and then the semaphore.  Made all functions of type pascal.  Added
  27.                                     checks for aSemaphore == NULL and if so return a threadProtocolErr where appropriate.
  28.                 2/5/93        bsp        Added CreateLinkedListSemaphore, DeleteLinkedListSemaphore, DeleteSimpleSemaphore and
  29.                                     CreateSimpleSemaphore. Also made DisposePtr w/in DeleteArraySemaphore a critical area.  
  30.                                     Changed functions to use LinkedListSemaphorePtr, aSimpleSemaphorePtr, and anArraySemaphorePtr.
  31.                 2/4/93        bsp        Added TestAndSetByte to get rid of trap calls to Begin & End, changed it to
  32.                                     'seq d0' to reflect changes made in header file.  Removed the ThreadBegin &
  33.                                     ThreadEnd from releaseSimpleSemaphore.  Enclosed the Critical area in 
  34.                                     releaseLinkedListSemaphore & releaseArraySemaphore.
  35.                 2/2/93        bsp        Added functions to handle semaphore models.
  36.                 1/31/93        ewa        New Today.
  37.                 
  38.         To Do:
  39.                 Figure out what we do if the app quits out from under us
  40.  
  41. */
  42.  
  43. #include        "ThreadUtil.h"
  44.  
  45. #pragma segment ThreadUtilities
  46.  
  47. /*
  48.  *    InitializeThreadUtilities()
  49.  *
  50.  *  INPUT:    void
  51.  *    RETURN: void
  52.  *  WHEN CAN BE CALLED:  From cooperative thread only
  53.  *
  54.  *  This function does nothing but force LoadSeg to load in this segment.
  55.  */
  56. pascal void InitializeThreadUtilities()
  57. {
  58.  
  59. }
  60.  
  61. #pragma parameter __D0 TestAndSetByte(__A0)
  62. short TestAndSetByte(Byte *pByte)
  63.  = {0x7000, 0x08D0, 0x0000, 0x57C0};
  64.  /*  moveq #0, d0
  65.   *  bset #0, (a0)        Test bit 0, if == 0, set to 1
  66.   *  seq d0                If set to 1, make all 1's else all 0's
  67.   */
  68.   
  69. /* structs  & defines */
  70. #define        kNumAttempts            3    // number of times to try and install the time task
  71.  
  72. #define        HELL_NOT_FROZEN_OVER    1    // used for while loops
  73.  
  74. /* Used by SimpleSemaphore Model */
  75. #define kIsAvailable                false    // value of the semaphore, since we use TestAndSetByte
  76. #define kNotAvailable                true    // valud of the semaphore, since we use TestAndSetByte
  77.  
  78. /* Used by Array Semaphore Model */
  79. #define kNobodyWaiting                -1
  80.  
  81. struct TMInfo {
  82.     TMTask            theTMTask;
  83.     ThreadTaskRef    theAppTask;
  84.     ThreadID        theThread;
  85.     unsigned long    theDelay;
  86.     short            numAttempts;
  87. };
  88. typedef struct TMInfo TMInfo;
  89. typedef TMInfo *TMInfoPtr;
  90.  
  91.  
  92. /* internal routine defines */
  93. pascal void            SleepTerminator (ThreadID threadDead, TMInfoPtr theTMTaskPtr);
  94. pascal TMInfoPtr    GetTMInfo (void) = {0x2E89};    /* move.l a1,(sp) */
  95. pascal void            WakeTMTask (void);
  96.  
  97. /* routine implementations */
  98.  
  99. /* 
  100.  *    DelayThread
  101.  *  
  102.  *  INPUT:    unsigned long
  103.  *  RETURN: void
  104.  *  WHEN CAN BE CALLED:  From cooperative or preemptive thread
  105.  *
  106.  *  This function sets up a time manager task to wake up the sleeping thread after a certain number of ticks.
  107.  *  It puts the thread to sleep and says to schedule anyone next.
  108.  *
  109.  *  SIDE EFFECTS:  Destroys any Thread Terminator task that existed for the thread.
  110.  */
  111. pascal void
  112. DelayThread (unsigned long millisecDelay) {
  113.     TMInfo        myTask;
  114.     OSErr        err;
  115.     
  116. /* fill in the record */
  117.     myTask.theTMTask.tmAddr = (TimerProcPtr)(WakeTMTask);
  118.     myTask.theTMTask.tmWakeUp = 0;
  119.     myTask.theTMTask.tmReserved = 0;
  120.     myTask.theDelay = millisecDelay;        /* in case we need to poll until the thread can sleep */
  121.     myTask.numAttempts = kNumAttempts;        /* re-prime this many times before giving up */
  122.  
  123. /* including the app task ref */
  124.     err = GetThreadCurrentTaskRef (&(myTask.theAppTask));
  125.     if (err)
  126.         return;
  127. /* and the current thread ID */
  128.     err = GetCurrentThread (&(myTask.theThread));
  129.     if (err)
  130.         return;
  131. /* Begin Critical section so we do not get bothered by busy-bodies after we have primed */
  132.     ThreadBeginCritical();
  133.     err = SetThreadTerminator (kCurrentThreadID, (ThreadTerminationProcPtr)(SleepTerminator), &myTask);
  134.  
  135. /* install, prime, and put us to sleep */
  136.     InsTime ((QElemPtr)&myTask);
  137.     PrimeTime ((QElemPtr)&myTask, millisecDelay);
  138.  
  139. /* Hopefully our timer does not fire before we can sleep... */
  140. /* ...but if it does, the TM task will re-prime and try again */
  141.     SetThreadStateEndCritical (kCurrentThreadID, kStoppedThreadState, kNoThreadID);
  142.     
  143. /* We are now awake, so lets blow this joint */
  144.     err = SetThreadTerminator (kCurrentThreadID, (ThreadTerminationProcPtr)(nil), nil);
  145.     RmvTime ((QElemPtr)&myTask);
  146. }
  147.  
  148. /*
  149.  *    SleepTerminator
  150.  *
  151.  *  INPUT:  ThreadID, TMInfoPtr
  152.  *  RETURN: void
  153.  *
  154.  *  This is the terminator task, it is called if the thread we put to sleep never wakes up before the thread
  155.  *  is terminated.  This removed the time manager task from the queue.
  156.  */
  157. pascal void 
  158. SleepTerminator (ThreadID threadDead, TMInfoPtr theTMTaskPtr) {
  159.     #pragma unused (threadDead)
  160.     RmvTime ((QElemPtr)theTMTaskPtr);
  161. }
  162.  
  163. /*
  164.  *  WakeTMTask
  165.  *
  166.  *  INPUT:  void
  167.  *  RETURN: void
  168.  *
  169.  *  This function is called by the time Manager to wake up the thread that we put to sleep with DelayThread.
  170.  */
  171. pascal void
  172. WakeTMTask (void) {
  173.     ThreadTaskRef    theAppTaskRef;
  174.     ThreadID        theThreadID;
  175.     ThreadState        theThreadState;
  176.     TMInfoPtr        theTMTaskPtr;
  177.     
  178.     /* get the record from register A1 before anything else */
  179.     theTMTaskPtr = GetTMInfo();    
  180.     
  181.     /* Get our data */
  182.     theAppTaskRef = theTMTaskPtr->theAppTask;
  183.     theThreadID = theTMTaskPtr->theThread;
  184.     
  185.     /* See if the thread is stopped yet */
  186.     GetThreadStateGivenTaskRef (theAppTaskRef, theThreadID, &theThreadState);
  187.     /* if it is awake, re-prime and try again */
  188.     if (theThreadState != kStoppedThreadState && theTMTaskPtr->numAttempts > 0)
  189.         {
  190.         (theTMTaskPtr->numAttempts)--;
  191.         PrimeTime ((QElemPtr)theTMTaskPtr, theTMTaskPtr->theDelay);        /* re-prime to try again! */
  192.         }
  193.     /* if it is sleeping, mark it for wake up! */
  194.     else
  195.         SetThreadReadyGivenTaskRef (theAppTaskRef, theThreadID);
  196. }
  197.  
  198.  
  199. /*
  200.  * Below are all the functions necessary for handling the three types of semaphore models.
  201.  * The simple SyncWait Model (SimpleSemaphore), Queue up with infinte number of elements in the queue
  202.  * (LinkedListSemaphore), and Fixed number of people in queue (ArraySemaphore).  For more information 
  203.  * on semaphores, read your favorite Operating Systems book.
  204.  */
  205.  
  206. /*
  207.  *  CreateSimpleSemaphore
  208.  *
  209.  *  INPUT:  SimpleSemaphorePtr *
  210.  *  RETURN: OSErr
  211.  *  WHEN CAN BE CALLED:  Only from a cooperative thread
  212.  *
  213.  *  This function initializes the semaphore to isAvailable.  Returns the respective MemError().
  214.  */
  215. pascal OSErr CreateSimpleSemaphore( SimpleSemaphorePtr *aSemaphore )
  216. {
  217.     ThreadBeginCritical();
  218.     
  219.     *aSemaphore = (SimpleSemaphore *) NewPtr(sizeof(SimpleSemaphore));
  220.     
  221.     if(*aSemaphore == NULL)
  222.     {
  223.         ThreadEndCritical();
  224.         return MemError();
  225.     }
  226.     
  227.     **aSemaphore = kIsAvailable;
  228.     
  229.     ThreadEndCritical();
  230.     
  231.     return noErr;
  232. }
  233.  
  234. /*
  235.  *  GetSimpleSemaphore
  236.  *
  237.  *  INPUT:  SimpleSemaphorePtr
  238.  *  RETURN: OSErr
  239.  *  WHEN CAN BE CALLED:  From cooperative and preemptive threads
  240.  *
  241.  *  This function just waits until the semaphore is available, when it is, we make it unavailable and
  242.  *  return.  Otherwise just keep Yielding to whomever else is around.
  243.  */
  244. pascal OSErr GetSimpleSemaphore(SimpleSemaphorePtr aSemaphore)
  245. {
  246.     if(aSemaphore == NULL)
  247.         return semaphoreButtHeadErr;
  248.         
  249.     while( HELL_NOT_FROZEN_OVER )
  250.     {
  251.         if( TestAndSetByte(aSemaphore) )    // if semaphore == isAvailable, set it to notAvailable
  252.         {
  253.             break;
  254.         } else {
  255.             YieldToAnyThread();
  256.         }
  257.     }
  258.     
  259.     return noErr;
  260. }
  261.  
  262. /* 
  263.  *  ReleaseSimpleSemaphore
  264.  *
  265.  *  INPUT:  SimpleSemaphorePtr
  266.  *  RETURN: OSErr
  267.  *  WHEN CAN BE CALLED:  From cooperative and preemptive threads
  268.  *
  269.  *  This function just sets the semaphore to available.  Returns an error if something wrong, else noErr.
  270.  */
  271.  
  272. pascal OSErr ReleaseSimpleSemaphore(SimpleSemaphorePtr aSemaphore)
  273. {    
  274.     if(aSemaphore == NULL)
  275.         return semaphoreButtHeadErr;
  276.         
  277.     *aSemaphore = kIsAvailable;            
  278.  
  279.     return noErr;
  280. }
  281.  
  282. /*
  283.  *  DeleteSimpleSemaphore
  284.  *
  285.  *  INPUT:  SimpleSemaphorePtr
  286.  *  RETURN: OSErr
  287.  *  WHEN CAN BE CALLED:  Only from a cooperative thread
  288.  *
  289.  *  This function disposes the semaphore, returning the respective MemError().  If the semaphore was NULL, 
  290.  *  return a semaphoreButtHeadErr.
  291.  */
  292. pascal OSErr DeleteSimpleSemaphore( SimpleSemaphorePtr aSemaphore )
  293. {
  294.     ThreadBeginCritical();
  295.     
  296.     if(aSemaphore != NULL)
  297.     {
  298.         DisposePtr( (Ptr) aSemaphore );
  299.         ThreadEndCritical();
  300.         return MemError();
  301.     }
  302.     
  303.     ThreadEndCritical();
  304.     return semaphoreButtHeadErr;
  305. }
  306.  
  307.  
  308. /*
  309.  *  CreateLinkedListSemaphore
  310.  *
  311.  *  INPUTS:  LinkedListSemaphorePtr *
  312.  *    RETURNS: OSErr
  313.  *  WHEN CAN BE CALLED:  Only from a cooperative thread
  314.  *
  315.  *  This function initializes the LinkedListSemaphore, returning the respective MemError().
  316.  */
  317.  
  318. pascal OSErr CreateLinkedListSemaphore( LinkedListSemaphorePtr *aSemaphore )
  319. {    
  320.     ThreadBeginCritical();
  321.     
  322.     *aSemaphore = (LinkedListSemaphore *) NewPtr (sizeof(LinkedListSemaphore));
  323.     
  324.     if(*aSemaphore == NULL)
  325.     {
  326.         ThreadEndCritical();
  327.         return MemError();
  328.     }
  329.     
  330.     (*aSemaphore)->waitingList = NULL;
  331.     (*aSemaphore)->lastInList = (*aSemaphore)->waitingList;
  332.     (*aSemaphore)->theHolder = kNoThreadID;
  333.     (*aSemaphore)->available = kIsAvailable;
  334.     (*aSemaphore)->busy = false;
  335.     
  336.     ThreadEndCritical();
  337.     
  338.     return noErr;
  339. }
  340.  
  341. /*
  342.  *  GetLinkedListSemaphore
  343.  *
  344.  *  INPUTS:  LinkedListElementPtr, LinkedListSemaphorePtr
  345.  *  RETURNS:  OSErr
  346.  *  WHEN CAN BE CALLED:  From cooperative and preemptive threads
  347.  *
  348.  *  The function checks to see if the semaphore is available.  If it is, then we grab it and return.
  349.  *  If the sempahore is not available, then we place ourself in the waiting queue and put ourselves
  350.  *  into the stopped mode (sleeping).  Return an error if somethings wrong, else noErr.
  351.  */
  352. pascal OSErr GetLinkedListSemaphore(LinkedListElementPtr whichOne, LinkedListSemaphorePtr aSemaphore )
  353. {
  354.     if(aSemaphore == NULL)
  355.         return semaphoreButtHeadErr;
  356.         
  357.     if(GetCurrentThread(&(whichOne->whoAmI)) != noErr)
  358.     {
  359.         DebugStr("\pFailed getting current Thread ID withing GetLinkedListSemaphore -- very bad");
  360.     }
  361.  
  362.     whichOne->next = NULL;
  363.     
  364.     while( !TestAndSetByte(&(aSemaphore->busy)) ) ;    // Spin & wait until semaphore isn't busy
  365.     
  366.     if( TestAndSetByte(&(aSemaphore->available)) )    // aSemaphore->available == true
  367.     {
  368.         aSemaphore->theHolder = whichOne->whoAmI;
  369.         aSemaphore->busy = false;    // we are safe to say we aren't busy, because can't be interrupted
  370.     }
  371.     else if(whichOne->whoAmI != aSemaphore->theHolder)
  372.     {
  373.         ThreadBeginCritical();
  374.                 
  375.         aSemaphore->busy = false;    // we are safe to say we aren't busy, because can't be interrupted
  376.         if(aSemaphore->waitingList == NULL)
  377.         {
  378.             aSemaphore->waitingList = whichOne;
  379.             aSemaphore->lastInList = aSemaphore->waitingList;
  380.         }
  381.         else
  382.         {
  383.             aSemaphore->lastInList->next = whichOne;
  384.             aSemaphore->lastInList = whichOne;
  385.         }
  386.         
  387.         SetThreadStateEndCritical(kCurrentThreadID, kStoppedThreadState, kNoThreadID);
  388.     }
  389.  
  390.     return noErr;    
  391. }
  392.  
  393. /*
  394.  *  ReleaseLinkedListSemaphore
  395.  *
  396.  *  INPUTS:  LinkedListSemaphorePtr
  397.  *    RETURNS: OSErr
  398.  *  WHEN CAN BE CALLED:  From cooperative and preemptive threads
  399.  *
  400.  *  This function releases the semaphore only if there is someone waiting on it.  If there
  401.  *  is then we release the semaphore, make the new holder the one who was waiting.  Pop him
  402.  *  off the waiting queue, make him ready and set our state to READY, and ask that the new 
  403.  *  owner of the semaphore be set to run.  Return an error if something is wrong else noErr.
  404.  */
  405. pascal OSErr ReleaseLinkedListSemaphore( LinkedListSemaphorePtr aSemaphore )
  406. {
  407.     if(aSemaphore == NULL)
  408.         return semaphoreButtHeadErr;
  409.         
  410.     while( !TestAndSetByte(&(aSemaphore->busy)) ) ;    // Spin & wait until semaphore isn't busy
  411.  
  412.     if(aSemaphore->waitingList != NULL)
  413.     {
  414.         ThreadBeginCritical();
  415.         
  416.         aSemaphore->busy = false;        // we are safe to say we aren't busy, because can't be interrupted        
  417.  
  418.         aSemaphore->theHolder = aSemaphore->waitingList->whoAmI;
  419.         aSemaphore->waitingList = aSemaphore->waitingList->next;
  420.                     
  421.         if(aSemaphore->waitingList == NULL)
  422.             aSemaphore->lastInList = NULL;
  423.             
  424.         SetThreadState(aSemaphore->theHolder, kReadyThreadState, kNoThreadID);
  425.  
  426.         SetThreadStateEndCritical(kCurrentThreadID, kReadyThreadState, aSemaphore->theHolder);
  427.     } else {
  428.         aSemaphore->available = kIsAvailable;
  429.         aSemaphore->busy = false;        // we are safe to say we aren't busy, because can't be interrupted        
  430.     }
  431.     
  432.     return noErr;
  433. }
  434.  
  435. /*
  436.  *  DeleteLinkedListSemaphore
  437.  *
  438.  *  INPUTS:  LinkedListSemaphorePtr
  439.  *    RETURNS: OSErr
  440.  *  WHEN CAN BE CALLED:  Only from a cooperative thread
  441.  *
  442.  *  This function deletes the LinkedListSemaphore, returning a valid one.  If the semaphore was == NULL
  443.  *  then return a semaphoreButtHeadErr.  If there are threads still waiting on the semaphore return a
  444.  *  semaphoreWaitErr.
  445.  */
  446. pascal OSErr DeleteLinkedListSemaphore( LinkedListSemaphorePtr aSemaphore )
  447. {
  448.     ThreadBeginCritical();
  449.     
  450.     if(aSemaphore != NULL)
  451.     {
  452.         if(aSemaphore->waitingList != NULL)
  453.         {
  454.             ThreadEndCritical();
  455.             return semaphoreWaitErr;
  456.         }
  457.         DisposePtr( (Ptr) aSemaphore);
  458.         ThreadEndCritical();
  459.         return MemError();
  460.     }
  461.     
  462.     ThreadEndCritical();
  463.     return semaphoreButtHeadErr;
  464. }
  465.  
  466.  
  467.  
  468. /*
  469.  *  CreateArraySemaphore
  470.  *
  471.  *  INPUT:  long, ArraySemaphorePtr
  472.  *  RETURN: OSErr
  473.  *  WHEN CAN BE CALLED:  Only from a cooperative thread
  474.  *
  475.  *    This function allocates space for the array that holds the list of Threads waiting for the
  476.  *  semaphore.  It also initializes all the data needed to use an Array Semaphore.  This returns
  477.  *  any error it may encounter.
  478.  */
  479. pascal OSErr    CreateArraySemaphore(long maxInQueue, ArraySemaphorePtr *aSemaphore)
  480. {
  481.     long index;
  482.         
  483.     /*  If maxInQueue is < 0, definitely an error.  If == 0, why are you wasting the space in
  484.      *  using this type a semaphore, the SimpleSemaphore model is better. */
  485.     if(maxInQueue <= 0)
  486.         return semaphoreButtHeadErr;
  487.     
  488.     ThreadBeginCritical();
  489.     
  490.     *aSemaphore = (ArraySemaphore *) NewPtr (sizeof(ArraySemaphore));
  491.     
  492.     if(*aSemaphore == NULL)
  493.     {
  494.         ThreadEndCritical();
  495.         return MemError();
  496.     }
  497.     
  498.     (*aSemaphore)->available = kIsAvailable;            
  499.     (*aSemaphore)->theHolder = kNoThreadID;        // default value == noHolder
  500.     (*aSemaphore)->whoseNext = kNobodyWaiting;    // default value == nobody next in line
  501.     (*aSemaphore)->nextSpot = 0;                
  502.     (*aSemaphore)->numberWaiting = 0;
  503.     
  504.     (*aSemaphore)->waitList = (ThreadID **) NewPtr ( sizeof(ThreadID *));
  505.     
  506.     if((*aSemaphore)->waitList == NULL)
  507.     {
  508.         ThreadEndCritical();
  509.         return MemError();
  510.     }
  511.     else
  512.     {
  513.         *((*aSemaphore)->waitList) = (ThreadID *) NewPtr (sizeof(ThreadID) * maxInQueue);
  514.         if( *((*aSemaphore)->waitList) == NULL)
  515.         {
  516.             ThreadEndCritical();
  517.             return MemError();
  518.         }
  519.             
  520.         /* Initialize all spots in the array to kNoThreadID, so we if somehow we think someone's waiting, this will
  521.          * will catch us if there's not...
  522.          */
  523.         for(index = 0; index < maxInQueue; index++)
  524.             (*((*aSemaphore)->waitList))[index] = kNoThreadID;
  525.     }
  526.     
  527.     (*aSemaphore)->maxInQueue = maxInQueue;
  528.     
  529.     ThreadEndCritical();
  530.     
  531.     return noErr;    
  532. }
  533.  
  534. /*
  535.  *  GetArraySemaphore
  536.  *
  537.  *  INPUT:  ArraySemaphorePtr
  538.  *  RETURN: OSErr
  539.  *  WHEN CAN BE CALLED:  From cooperative and preemptive threads
  540.  *
  541.  *  This function attempts to get the semaphore.  If it is available, we mark it unavailable and return.
  542.  *  If it is not and there are spaces left in the waiting list, then queue ourselves up and put ourself
  543.  *     to sleep.  If there is no space in the queue then we "sit & spin", yielding to everyone until a spot 
  544.  *  opens in the queue for us to place ourselves, or grab the semaphore....  Return an error if pass in
  545.  *  a NULL Ptr, else noErr.
  546.  */
  547.  
  548. pascal OSErr GetArraySemaphore( ArraySemaphorePtr aSemaphore )
  549. {
  550.     ThreadID    whoAmI;
  551.  
  552.     if(aSemaphore == NULL)
  553.         return semaphoreButtHeadErr;
  554.         
  555.     ThreadBeginCritical();
  556.  
  557.     while(aSemaphore->numberWaiting == aSemaphore->maxInQueue)
  558.     {
  559.         // If all the spots in the queue are filled up, then End the critical section and Yield
  560.         ThreadEndCritical();
  561.         YieldToAnyThread();
  562.         
  563.         // After we return from the yield we must start up the critical section again
  564.         ThreadBeginCritical();
  565.     }
  566.     
  567.     if(GetCurrentThread(&whoAmI) != noErr)
  568.     {
  569.         DebugStr("\pFailed getting current Thread ID for whoAmI");
  570.     }
  571.  
  572.     if( TestAndSetByte(&(aSemaphore->available)) )    
  573.     {
  574.         if(GetCurrentThread(&(aSemaphore->theHolder)) != noErr)
  575.         {
  576.             DebugStr("\pFailed getting current Thread ID for the holder");
  577.         }
  578.         ThreadEndCritical();
  579.     }
  580.     else if(whoAmI != aSemaphore->theHolder)
  581.     {                
  582.         (*(aSemaphore->waitList))[aSemaphore->nextSpot] = whoAmI;
  583.             
  584.         if(aSemaphore->whoseNext == kNobodyWaiting)
  585.             aSemaphore->whoseNext = aSemaphore->nextSpot;
  586.         
  587.         if( (aSemaphore->nextSpot + 1) < aSemaphore->maxInQueue)
  588.             aSemaphore->nextSpot++;
  589.         else
  590.             aSemaphore->nextSpot = 0;
  591.                 
  592.         aSemaphore->numberWaiting++;
  593.         
  594.         SetThreadStateEndCritical(kCurrentThreadID, kStoppedThreadState, kNoThreadID);
  595.     }
  596.     
  597.     return noErr;
  598. }
  599.  
  600. /*
  601.  *  ReleaseArraySemaphore
  602.  *
  603.  *  INPUT:  ArraySemaphorePtr
  604.  *  RETURN: OSErr
  605.  *  WHEN CAN BE CALLED:  From cooperative and preemptive threads
  606.  *
  607.  *  This funtion releases the semaphore only if Threads are waiting for it.  If this is the case, then the next
  608.  *  Thread in the waiting List is marked as the holder, woken up and current Thread is set to the Ready State, 
  609.  *  telling the Thread Manager to now make the holder of the semaphore the current running Thread.  Return an
  610.  *  error if any problems occur.
  611.  */
  612.  
  613. pascal OSErr ReleaseArraySemaphore( ArraySemaphorePtr aSemaphore)
  614. {
  615.     if(aSemaphore == NULL)
  616.         return semaphoreButtHeadErr;
  617.         
  618.     if(aSemaphore->whoseNext != kNobodyWaiting)
  619.     {    
  620.         ThreadBeginCritical();
  621.         
  622.         aSemaphore->theHolder = (*(aSemaphore->waitList))[aSemaphore->whoseNext];
  623.         aSemaphore->numberWaiting--;
  624.         if(aSemaphore->numberWaiting != 0)
  625.         {
  626.             aSemaphore->whoseNext++;
  627.             if(aSemaphore->whoseNext >= aSemaphore->maxInQueue)
  628.                 aSemaphore->whoseNext = 0;
  629.                 
  630.             if(aSemaphore->whoseNext == aSemaphore->nextSpot)
  631.                 aSemaphore->whoseNext = kNobodyWaiting;
  632.             
  633.         }
  634.         
  635.         SetThreadState(aSemaphore->theHolder, kReadyThreadState, kNoThreadID);
  636.  
  637.         SetThreadStateEndCritical(kCurrentThreadID, kReadyThreadState, aSemaphore->theHolder);
  638.     } else
  639.         aSemaphore->available = kIsAvailable;
  640.  
  641.     return noErr;
  642. }
  643.  
  644. /*
  645.  *  DeleteArraySemaphore
  646.  *
  647.  *  INPUT:  ArraySemaphorePtr
  648.  *  RETURN: OSErr
  649.  *  WHEN CAN BE CALLED:  Only from a cooperative thread
  650.  *
  651.  *  This function frees the memory used to allocate the array of waiting Threads.  It returns the MemError
  652.  *  funtion, which will return an error if we recieved one with the DisposePtr call.  If the semaphore == NULL
  653.  *  then return a semaphoreButtHeadErr.  If there are threads waiting on the semaphore, return semaphoreWaitErr.
  654.  */
  655.  
  656. pascal OSErr DeleteArraySemaphore( ArraySemaphorePtr aSemaphore )
  657. {
  658.     OSErr    theError;
  659.     
  660.     ThreadBeginCritical();
  661.     
  662.     if(aSemaphore != NULL)
  663.     {
  664.         if(aSemaphore->numberWaiting != 0)
  665.         {
  666.             ThreadEndCritical();
  667.             return semaphoreWaitErr;
  668.         }
  669.         
  670.         DisposePtr( (Ptr) (*(aSemaphore->waitList)) );
  671.         theError = MemError();
  672.         
  673.         if(theError == noErr)
  674.         {
  675.             DisposePtr( (Ptr) (aSemaphore->waitList) );
  676.             theError = MemError();
  677.             if(theError == noErr)
  678.             {
  679.                 DisposePtr( (Ptr) aSemaphore );
  680.                 theError = MemError();
  681.             }
  682.         }
  683.         
  684.         ThreadEndCritical();
  685.         return theError;    
  686.     }
  687.     
  688.     ThreadEndCritical();
  689.     
  690.     return semaphoreButtHeadErr;
  691. }
  692.